#include "PWM.h"
#include "Timer.h"
#include "adc.h"
#include "infrared.h"
#include "light.h"
#include "w55mh32_adc.h"
#include "w55mh32_gpio.h"
#include "w55mh32_rcc.h"
#include <stdbool.h>
#include <stdio.h>

// Light control parameters
#define FULL_BRIGHTNESS 100 // Full brightness
#define LIGHT_OFF 0         // Turn off light

/**
 * Global variables
 */
uint16_t g_light_intensity; // Global light intensity
bool g_is_person_detected =
    false; // Infrared sensor detection status (true=person detected)
uint16_t g_pwm_duty_cycle =
    0;                      // PWM duty cycle (0-100, controls LED brightness)
uint16_t g_mode_switch = 1; // Mode selection (0=manual mode, 1=auto mode)

extern uint16_t g_low_bright; // LED low brightness setting in auto mode (0-100)
extern uint16_t
    g_light_thres_low; // Light threshold lower limit (for auto mode judgment)

/**
 * Static variables
 */
static bool s_prev_person_state =
    false; // Previous person detection state (for state change detection)
static bool s_delay_active = false; // Delay state flag (true=in delay phase)
static uint32_t s_delay_timer =
    0; // Delay timer (unit: ms, for 10-second delay logic)

/**
 * @brief Initialize light sensor (configure PA4 as analog input mode)
 */
void light_init(void) {
  GPIO_InitTypeDef gpio_init_structure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

  gpio_init_structure.GPIO_Pin = GPIO_Pin_4;
  gpio_init_structure.GPIO_Mode =
      GPIO_Mode_AIN; // Analog input mode (for ADC sampling)
  GPIO_Init(GPIOA, &gpio_init_structure);
}

/**
 * @brief Read light intensity (convert AD value to physical quantity)
 * @return Light intensity value (calculated from filtered AD value)
 */
float read_light_intensity(void) {
  ad_filter_process();                   // Get filtered AD value
  uint16_t raw = g_ad_filtered_value[1]; // Take filtered value of channel 1
  if (raw == 0) {
    raw = 1; // Avoid division by zero error
  }
  return 500000.0f / (float)raw; // Convert to physical quantity (formula
                                 // depends on sensor characteristics)
}

/**
 * @brief Read raw AD value from light sensor (unfiltered)
 * @return Raw AD value (real-time sampling value of channel 1)
 */
uint16_t light_read_raw(void) {
  return g_ad_value[1]; // Directly return raw AD value of channel 1
}

/**
 * @brief Intelligent light control logic
 * @note Auto mode logic:
 *       1. Low light:
 *          - Person detected -> Full brightness; No person -> Delay 10 seconds
 * then low light (adjustable brightness)
 *       2. High light -> Turn off light
 *       3. Other cases -> Maintain current brightness
 *       Manual mode: Controlled by external commands, no additional processing
 * in this function
 */
void light_control(void) {
  g_light_intensity = (int)read_light_intensity(); // Update light intensity
  g_is_person_detected = infrared_get_data(); // Update person detection status
  uint16_t light_thres_high =
      g_light_thres_low + 50; // Light threshold upper limit

  if (g_mode_switch) // Auto mode
  {
    // Person leaves (from present to absent): Activate delay
    if (s_prev_person_state && !g_is_person_detected) {
      s_delay_active = true;
      s_delay_timer = 10000; // 10 second delay
    }

    // Person arrives (from absent to present): Cancel delay
    if (!s_prev_person_state && g_is_person_detected) {
      s_delay_active = false;
    }

    // Light logic processing
    if (g_light_intensity <= g_light_thres_low) {
      if (g_is_person_detected) {
        g_pwm_duty_cycle =
            FULL_BRIGHTNESS; // Person detected -> Full brightness
      } else if (s_delay_active) {
        g_pwm_duty_cycle =
            FULL_BRIGHTNESS; // During delay -> Maintain full brightness
      } else {
        g_pwm_duty_cycle = g_low_bright; // Delay ended -> Low light
      }
    } else if (g_light_intensity >= light_thres_high && !g_is_person_detected) {
      s_delay_active = false;
      g_pwm_duty_cycle =
          LIGHT_OFF; // High light and no person -> Turn off light
    }
    // Other cases maintain current brightness
  }
  // Manual mode: Controlled by external commands, no processing needed

  s_prev_person_state = g_is_person_detected; // Update previous state
  pwm_set_compare1(g_pwm_duty_cycle);         // Apply PWM duty cycle
}

/**
 * @brief TIM2 interrupt service function (handle delay timing)
 * @note Triggered every 1ms, decrement delay timer, end delay when reaching 0
 */
void TIM2_IRQHandler(void) {
  if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) {
    if (s_delay_active && s_delay_timer > 0) {
      s_delay_timer--;
      if (s_delay_timer == 0) {
        s_delay_active = false; // Delay ended
      }
    }
    TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // Clear interrupt flag
  }
}
